package master.common.encoding;

import java.util.Arrays;

import master.common.utils.ObjectUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 编码级次工具
 */
public class CodeGradeUtil {
	private static final Logger logger = LoggerFactory.getLogger(CodeGradeUtil.class);
	
	/**编码字符类型*/
	/**数字*/
	public static final int DIGIT = 1;
	/**字母*/
	public static final int LETTER = 2;
	/**数字和字母*/
	public static final int DIGIT_AND_LETTER = 3;
	
	/**编码规则*/
    private String codeRule;
    /**编码字段分段信息*/
    private int[] codeSection = null;
    /**各级编码长度*/
    private int[] codeLength = null;
    /**完整编码长度*/
    private int fullGradeLength;
    /**编码补齐字符*/
    private Character c;
    /**编码字符类型（数字、字母、数字+字母）*/
    private int charType;
    
    /**
	 * @return the charType
	 */
	public int getCharType() {
		return charType;
	}

	/**
     * 根据编码规则创建编码级次工具
     * @param codeRule
     */
    public CodeGradeUtil(String codeRule){
    	this(codeRule, null, DIGIT_AND_LETTER);
    }
    
    /**
     * 根据编码级次和补齐字符创建编码级次工具
     * @param codeRule
     * @param c
     */
	public CodeGradeUtil(String codeRule, Character c) {
		this(codeRule, c, DIGIT_AND_LETTER);
	}
	
    /**
     * 根据编码规则和字符类型创建编码级次工具
     * @param codeRule
     */
    public CodeGradeUtil(String codeRule, int charType){
    	this(codeRule, null, charType);
    }
    
    /**
     * 根据编码级次、补齐字符和字符类型创建编码级次工具
     * @param codeRule
     * @param c
     */
	public CodeGradeUtil(String codeRule, Character c, int charType) {
		this.codeRule = codeRule;
		this.c = c;
		this.charType = charType;
		if ((charType == DIGIT && c != null && !Character.isDigit(c))
			||(charType == LETTER && c!= null && !Character.isLetter(c))) {
			throw new IllegalArgumentException("编码规则的补齐字符和字符类型冲突");
		}
		parseCodeRule();
	}
	
	/**
     * 得到该编码所属的级次。<br>
     * 如编码规则为"2/2/2/2"，则：<br>
     * 编码"101010"的级次为3，<br>
     * 编码"101"的级次为2（即使它实际上可能不规整）。
     * 如果有补齐字符，比如补齐字符为"0"
     * 编码"10101000"的级次为3，<br>
     * 编码"10100000"的级次为2
     * @return int
     * @param code java.lang.String
     */
    public int getCodeGrade(String code) {
        if (code == null || codeSection == null || code.length() == 0) {
            return 0;
        }
        if (c == null) {
			int length = code.length();
			int index = Arrays.binarySearch(codeLength, length);
			if (index >=0) {
				return index+1;
			} else {
				return -index;
			}
		} else {
			int index = getMaxGradeCount()-1;
			int gradeLength = fullGradeLength;
			int codeLength = code.length();
			
			outer:
			while (true) {
				if(codeLength <= gradeLength-codeSection[index]){					
					gradeLength -= codeSection[index];
					index--;
					continue;
				}
				for (int i = Math.max(codeLength, gradeLength)-1; i >= gradeLength-codeSection[index]; i--) {
					if (code.charAt(i) != c.charValue()) {
						break outer;
					}
				}
				gradeLength -= codeSection[index];
				index--;
			}
			
			return index+1;
        }
    }
    
    /**
	 * 返回两个编码共同部分的级次。<br>
	 * 如编码规则为"2/2/2/2"，则：<br>
	 * 编码"10101"和"101010"的相同部分级次为2，<br>
	 * 编码"100110"和"101010"的相同部分级次为1。<br>
	 * 
	 * @param code1
	 *            String
	 * @param code2
	 *            String
	 * @return int
	 */
    public int getEqualGrade(String code1, String code2) {
    	if(code1 == null || code2 == null){
    		return 0;
    	}
        
        code1 = normalizeCode(code1);
        code2 = normalizeCode(code2);
        if (ObjectUtils.equals(code1, code2)) {
            return getMaxGradeCount();//按理说这里应该返回getCodeGrade(code1)，为了兼容分组统计返回最大编码级次
        }
        //得到相等的位数
        int equalBits = 0;
        int minLength = Math.min(code1.length(), code2.length());
        for (int i = 0; i < minLength; i++) {
            if (code1.charAt(i) == code2.charAt(i)) {
                equalBits++;
            } else {
                break;
            }
        }
        //得到相同的级次
        int equalGrade = Arrays.binarySearch(codeLength, equalBits);
        if (equalGrade >= 0) {
			return equalGrade+1;
		} else {
			return -equalGrade-1;
		}
    }
    
    /**
     * 获取有效编码
     * 这个方法主要是针对有补齐字符的编码规则而言，去除冗余的补齐字符得到剩余的编码
     * @param code
     * @return 有效编码
     */
    public String getEffectiveCode(String code){
    	if (c == null || code == null || code.length() == 0) {
			return code;
		} else {
			int length = getGradeLength(getCodeGrade(code));
			return code==null?null:code.substring(0, length);
		}
    }
    
    /**
     * 得到完整grade级编码的长度（不包含补齐字母的长度）。<br>
     * 如编码规则为"2/2/2/2"，则：<br>
     * 2级编码的程度为4，<br>
     * 3级编码的程度为6。<br>
     * @param grade int 1代表第一级，依次类推
     * @return int
     */
    public int getGradeLength(int grade) {
    	if (grade <= 0) {
			return 0;
		} else {
			return codeLength[--grade];
		}
    }
    
    /**
     * 得到编码第grade级片段的长度。<br/>
     * 如编码规则为"2/3/3/2"，则：<br/>
     * 2级编码片段长度为3，<br/>
     * 4级编码片段长度为2。<br/>
     * @param grade int 1代表第一级，依次类推
     * @return int
     */
    public int getSectionLength(int grade) {
    	if (grade <= 0) {
			return 0;
		} else {
			return codeSection[--grade];
		}
    }
    
    /**
     * 得到该编码的grade级编码。<br>
     * 如编码规则为"2/2/2/2"，则：<br>
     * 编码"10101010"的3级编码为"101010"，<br>
     * 编码"10101"的3级编码为"10101"。
     * @return java.lang.String
     * @param code java.lang.String
     * @param grade int
     */
    public String getSubCode(String code, int grade) {
    	if (code == null || code.length() == 0) {
			return code;
		}
        String levelCode = "";
        if (codeSection != null) {
            if (grade > codeSection.length) {
                grade = codeSection.length;
            }
            int length = getGradeLength(grade);

            if (length > code.length()) {
                levelCode = code;
            }
            else {
            	if (c == null) {
            		levelCode = code.substring(0, length);
				} else {
					//如果code不规整，则需补齐
					StringBuilder strBuilder = new StringBuilder(fullGradeLength);
					strBuilder.append(code, 0, length);
					for (int i = length, in=fullGradeLength; i < in; i++) {
						strBuilder.append(c);
					}
					levelCode = strBuilder.toString();
				}
            }
        }
        return levelCode;
    }
    
    /**
     * 得到编码的占位字符
     * @return Character
     */
    public Character getPlaceHolder() {
    	return c;
    }
    
    /**
     * 将编码按编码级次解析成分段码。<br>
     * 如编码规则为"2/2/2/2"，则：<br>
     * 编码"100101"会解析成一个包含元素"10","01","01",null的字符数组，<br>
     * 编码"10010"会解析成一个包含元素"10","01","0",null的字符数组，<br>
     * 返回数组的长度和编码最大级次是相同的，编码长度不够的，对应<br>
     * 级次之后的数组元素为null(如果是补齐编码，则对应级次之后的数组元素是补齐字符)。
     * @param code String
     * @return String[]
     */
    public String[] parseCodeByRule(String code) {
    	if(codeSection == null){
    		return null;
    	}
        String[] codeSect = new String[codeSection.length];
        if (code != null) {
        	code = normalizeCode(code);
            int begin = 0;
            int length = code.length();

            for (int i = 0; i < codeSect.length; i++) {
                if (codeLength[i] < length) {
                    codeSect[i] = code.substring(begin, codeLength[i]);
                    begin = codeLength[i];
                } else {
                    codeSect[i] = code.substring(begin);
                    break;
                }
            }
        }
        return codeSect;
    }
    
	/**
	 * 得到最大编码级次
	 * @return 最大编码级次
	 */
	public int getMaxGradeCount(){
		return codeSection == null?0:codeSection.length;
	}
    
	/**
     * 解析编码规则。
     */
    private void parseCodeRule() {
        java.util.StringTokenizer st =
            new java.util.StringTokenizer(codeRule, " ,./\\", false);
        int count = st.countTokens();
        codeSection = new int[count];
        codeLength = new int[count];
        int index = 0;
        fullGradeLength = 0;
        try {
            while (st.hasMoreTokens()) {
                codeSection[index] = Integer.parseInt(st.nextToken());
                if (codeSection[index]<=0) {
					throw new IllegalArgumentException("编码规则设置有误");
				}
                fullGradeLength += codeSection[index];
                codeLength[index++] = fullGradeLength;
            }
        } catch (Exception e) {
            logger.error("解析编码规则出错", e);
            codeRule = null;
            codeSection = null;
            codeLength = null;
            fullGradeLength = 0;
        }
    }
    
    private String normalizeCode(String code){
    	if (c == null || code.length() == fullGradeLength) {
			return code;
		}
    	StringBuilder strBuilder = new StringBuilder(fullGradeLength);
    	strBuilder.append(code);
    	for (int i = code.length(); i < fullGradeLength; i++) {
    		strBuilder.append(c);
		}
    	return strBuilder.toString();
    }
    
    public static String normalizeCodeRule(String codeRule) {
    	if (codeRule == null) {
			throw new IllegalArgumentException("编码规则不能为null");
		}
    	return codeRule.replaceAll("[ ,.\\\\]", "/");
    }
}